import java.util.ArrayList;
import java.util.Iterator;

import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;


public class BeatModel implements BeatModelInterface, MetaEventListener{
	Sequencer sequencer;
	Sequence sequence;
	Track track;
	ArrayList beatObservers = new ArrayList();
	ArrayList bpmObservers = new ArrayList();
	int bpm = 90;

	

	
	public void initialize(){
		setUpMid();
		buildTrackAndStart();
	}
	
	public void on(){
		sequencer.start();
		setBPM(90);
	}
	
	public void off(){
		setBPM(0);
		sequencer.stop();		
	}
	
	public void setBPM(int bpm){
		this.bpm = bpm;
		sequencer.setTempoInBPM(getBPM());
		notifyBPMObservers();	
	}
	
	public int getBPM(){
		return bpm;		
	}
	
	void notifyBPMObservers() {
//		Iterator iter = beatObservers.iterator(); 
//		while ( iter.hasNext())
//		{
//			BeatObserver obj = (BeatObserver) iter.next();
//			obj.updateBPM();
//		}
		
		Iterator iter2 = bpmObservers.iterator(); 
		while ( iter2.hasNext())
		{
			BPMObserver obj = (BPMObserver) iter2.next();
			obj.updateBPM();
		}
	}
	
	void notifyBeatObservers() {
		Iterator iter = beatObservers.iterator(); 
		while ( iter.hasNext())
		{
			BeatObserver obj = (BeatObserver) iter.next();
			obj.updateBeat();
		}
	}
	
	
	void beatEvent(){
		notifyBeatObservers();	
	}
	
	void buildTrackAndStart(){
		int [] trackList = { 35, 0, 45, 0 };
		sequence.deleteTrack(null);
		track = sequence.createTrack();
		
		makeTracks( trackList);
		track.add(makeEvent(192,9,1,0,4));
		
		try{
			sequencer.setSequence(sequence);
		}catch(Exception e)
		{
			e.printStackTrace();
		}		
	}
	
	void makeTracks( int[] list){
		for ( int i=0; i < list.length; i++ ){
			int key = list[i];
			
			if( key != 0 ){
				track.add(makeEvent(144,9,key,100,i));
				track.add(makeEvent(128,9,key,100,i+1));
			}
				
		}
	}
	
	MidiEvent makeEvent( int comd, int chan, int one, int two, int tick ){
		MidiEvent event = null;
		try{
			ShortMessage a = new ShortMessage();
			a.setMessage(comd, chan, one, two);
			event = new MidiEvent(a, tick);
		}catch( Exception e ){
			e.printStackTrace();
		}
		
		return event;
		
	}
	void setUpMid(){
		try{
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.addMetaEventListener(this);
			sequence = new Sequence(Sequence.PPQ,4);
			track = sequence.createTrack();
			sequencer.setTempoInBPM(getBPM());			
		}catch( Exception e ){
			e.printStackTrace();
		}
		
	}	

	public void registerObserver( BPMObserver aObject ){
		bpmObservers.add( aObject );
	}
	
	public void registerObserver( BeatObserver aObject ){
		beatObservers.add( aObject );
	}

	@Override
	public void meta(MetaMessage meta) {
		// TODO Auto-generated method stub
		if ( meta.getType() == 47 )
		{
			beatEvent();
			sequencer.start();
			setBPM(getBPM());
		}		
	}
	
}
